
import hashlib
from datetime import datetime
from dateutil import rrule
import sys

from sqlalchemy import *
from sqlalchemy.orm import relation, sessionmaker, scoped_session, mapper
import pkg_resources
from timesched import Scheduler
from plugins import GenericEventLoader, Item

hash_pass = hashlib.md5 # sha1, sha224, sha256, sha384, sha512 . . .

metadata = MetaData()

########################################
## Main tables 
########################################
t_users = Table('users', metadata,
              Column('id', Integer, primary_key=True),
              Column('name', String),
              Column('description', String),
              Column('password', String),
              Column('is_group', Boolean)
              )

t_channels = Table('channels', metadata,
              Column('id', Integer, primary_key=True),
              Column('title', String,unique=True,nullable=False),
              Column('description', String,default=''),
              Column('maxdays', Integer,default=0)
              )

t_eventfactories = Table('event_factories', metadata,
              Column('id', Integer, primary_key=True),
              Column('channel_id', Integer, ForeignKey('channels.id')),
              Column('last_check', DateTime),
              Column('event_loader', PickleType)
              )

t_events = Table('events', metadata,
              Column('id', Integer, primary_key=True),
              Column('channel_id', Integer, ForeignKey('channels.id')),
              Column('title', String),
              Column('content', String),
              Column('author', String),
              Column('date', DateTime, default=datetime.now)
              )

########################################
## Many to many relation tables
########################################
t_users_groups = Table('users_groups', metadata,
                     Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
                     Column('group_id', Integer, ForeignKey('users.id'), primary_key=True),
                     )

t_events_to_read_by_users = Table('events_to_read', metadata,
                     Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
                     Column('event_id', Integer, ForeignKey('events.id'), primary_key=True),
                     )

t_events_read_by_users = Table('events_read', metadata,
                     Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
                     Column('event_id', Integer, ForeignKey('events.id'), primary_key=True),
                     )

t_groups_can_write_channels = Table('groups_can_write', metadata,
                     Column('group_id', Integer, ForeignKey('users.id'), primary_key=True),
                     Column('channel_id', Integer, ForeignKey('channels.id'), primary_key=True),
                     )

t_groups_can_read_channels = Table('groups_can_read', metadata,
                     Column('group_id', Integer, ForeignKey('users.id'), primary_key=True),
                     Column('channel_id', Integer, ForeignKey('channels.id'), primary_key=True),
                     )

########################################
## Objects
########################################
class Channel(object):
    """channel object"""

    def __init__(self,title,description,maxDays=0):
        self.title=title
        self.description=description
        self.maxDays=maxDays

    def __repr__(self):
        return "Channel %r" % (self.title,)

class EventFactory(object):
    """event factory object"""

    def __init__(self,event_loader):
        self.event_loader=event_loader
        self.last_check=None
        self.status=""

    def __repr__(self):
        return "EventFactory %s" % self.event_loader.title

class Event(object):
    """event object"""
    def __init__(self,title,content,author,channel_id,date=None):
        self.title=title
        self.content=content
        self.author=author
        self.date=date
        self.channel_id=channel_id
        if not self.date: self.date = datetime.now()
        
    def __repr__(self):
        return "Event %r" % (self.title,)

class User(object):
    """user object"""

    def __init__(self,name,password,is_group,description=''):
        self.name = name
        self.password = hash_pass(password).hexdigest()
        self.description = description
        self.is_group=is_group

    def checkPassword(self,password):
        return self.password == hash_pass(password).hexdigest()

    def __repr__(self):
        if self.is_group:
            s = "Group %r"
        else:
            s = "User %r"
        return s % (self.name,)

########################################
## Mappers
########################################

mapper(User, t_users, properties={
    'groups': relation(User,secondary=t_users_groups,
                       primaryjoin=t_users.c.id==t_users_groups.c.user_id,
                       secondaryjoin=t_users_groups.c.group_id==t_users.c.id,
                       backref='users')
                                }
       )

mapper(Channel, t_channels, properties={
    'events' : relation(Event,backref='channel',cascade="all"),
    'eventFactories' : relation(EventFactory,backref='channel',cascade="all"),
    'groups_can_write' : relation(User,secondary=t_groups_can_write_channels, backref='channel_can_write'),
    'groups_can_read' : relation(User,secondary=t_groups_can_read_channels, backref='channel_can_read')
                                       }
       )

mapper(EventFactory, t_eventfactories)

mapper(Event, t_events, properties={
    'users_to_read' : relation(User,secondary=t_events_to_read_by_users, backref='events_to_read'),
    'users_read' : relation(User,secondary=t_events_read_by_users, backref='events_read')
                                   }
       )

########################################
## Main
########################################
class Logger(object):
    """default log scheduler error:

use this as a prototype for your own logging object"""
    def __init__(self):
        """create the logger"""
    def __call__(self,event,t,v):
        """log"""
        raise


class Main(object):
    def __init__(self,pathDb):
        # init the db
        self.pathDb = pathDb
        engine = create_engine('sqlite:///%s' % self.pathDb, echo=False)
        metadata.create_all(engine)
        
        self.Session = scoped_session(sessionmaker(bind=engine))
        #check if administrator is needed
        session=self.Session()
        if not session.query(User).count():
            session.add(User("admin","password",False))           
            session.commit()
        #init and start the scheduler
        self.scheduler=Scheduler(logErrors=Logger())
        self.scheduler.start()
        #load plugin
        self.plugins=self._loadPlugin()

        #load eventFactories and close session
        #session=self.Session()
        self.efSched={}
        eventFactories=session.query(EventFactory).all()
        session.close()
        for ef in eventFactories:
            self._startEF(ef)
        
    def _loadPlugin(self):
        ENTRYPOINT = 'al_earth.plugin'
        classes = [ep.load() for ep in pkg_resources.iter_entry_points(ENTRYPOINT)]
        return dict([(c.event_type, c)  for c in classes])

    def _startEF(self,ef):
        self.efSched[ef.id]=self.scheduler.enter(self._getNewEvents,
                                                 args=(ef.id,),
                                                 when=ef.event_loader.recursion,
                                                 lastTime=ef.last_check)

    def _stopEF(self,ef):
        self.scheduler.cancel(self.efSched[ef.id])
        del(self.efSched[ef.id])

    def _getNewEvents(self,efId):
        session=self.Session()
        eventFactory=session.query(EventFactory).get(efId)
        eventFactory.last_check = datetime.now()
        try:
            newItems=eventFactory.event_loader.getItems()
            session.add_all([Event(i.title,
                                   i.content,
                                   i.author,
                                   eventFactory.channel_id,
                                   i.date) for i in newItems])

            if eventFactory.event_loader.isDead():
                self._stopEF(eventFactory)
                del(eventFactory)

            session.commit()      
        except:
            return
        session.close()


    def close(self):
        self.scheduler.stop()


if __name__=="__main__":

    import os
    try:    
        os.remove('test.db')
    except:
        pass
 #   import pdb ; pdb.set_trace()
    m=Main('test.db')
### populate db
    session=m.Session()

    john = User("John","password",False)
    rosy = User("Rosy","password",False)
    morris = User("Morris","password",False)
    janet = User("Janet","password",False)

    coworkers = User("coworkers","",True)
    family = User("family","",True)

    family.users.append(john)
    family.users.append(rosy)
    coworkers.users.append(john)
    coworkers.users.append(morris)
    coworkers.users.append(janet)

    session.add(john)
    session.add(rosy)
    session.add(morris)
    session.add(janet)
    session.add(coworkers)
    session.add(family)
    
# add channel
    jobchannel=Channel("job channel","all job related events")
    jobchannel.groups_can_write.append(coworkers)
    session.add(jobchannel)

    homechannel=Channel("home channel","birtdays and appointments")
    homechannel.groups_can_write.append(family)
    session.add(homechannel)

# add two event factory
    config={}
    config['title'] = "say hello 1"
    config['message']="Hello coworkers only once!"
    config['rruleset']=[{'rec':6,
                         'exclude': False,
                         'options':{'count':1}
                         }]
    eventloader=m.plugins['Note']
    new=EventFactory(eventloader(config))

    config={}
    config['title'] = "say hello 2"
    config['message']="Hello twice to  my family!"
    config['rruleset']=[{'rec':6,
                         'exclude': False,
                         'options':{'count':2}
                         }]
    eventloader=m.plugins['Note']
    new2=EventFactory(eventloader(config))

    jobchannel.eventFactories.append(new)
    homechannel.eventFactories.append(new2)
    session.commit()
    m._startEF(new)
    m._startEF(new2)
    
    event1 = Event("I'm coming . . .","see you soon !","John",homechannel.id)
    event2 = Event("write your report!","I'm watching you ! :-)","John",jobchannel.id)
    session.add(event1)
    session.add(event2)
    morris.events_to_read.append(event2)
    janet.events_read.append(event2)
    #~ #homechannel.events.append(event)

    session.commit()
    session.close()

# query db
    import time
    time.sleep(5)
    
    session = m.Session()
    #channel per user/group (permission)
    #event per user
    # user/channel permissions 
    for user in session.query(User).filter_by(is_group=False).all():
        groups = set(user.groups)
        groups.add(user)
        for channel in session.query(Channel).all():
            if groups & set(channel.groups_can_write):
                print "%s can write %s" %  (user.name, channel.title)
                for e in channel.events:
                    print "- %s" % e,
                    if e in user.events_to_read:
                        print " - TO READ"
                    elif e in user.events_read:
                        print " - READ"
                    else:
                        print " - TO NOTIFY"
            elif groups & set(channel.groups_can_read):
                print "%s can read %s" % (user.name, channel.title)
                for e in channel.events:
                    print "- %s" % e,
                    if e in user.events_to_read:
                        print " - TO READ"
                    elif e in user.events_read:
                        print " - READ"
                    else:
                        print " - TO NOTIFY"

    session.close()

    
